#pragma once
#include <iostream>
#include <string>
#include <sys/select.h>
#include <algorithm>
#include "Log.hpp"
#include "Socket.hpp"

static const int defualtport = 8888;
static const int defualtlog = 8;
static const int N = sizeof(fd_set) *8;//大小设置成位图结构的大小 *8(1byte = 8bit)

class SelectServer
{
private:
    void HandlerEvent(fd_set& rfds)
    {
        for(int i = 0; i< N ;i++)
        {

            if(_rfds_arry[i] == nullptr) continue;
            int fd = _rfds_arry[i]->GetSockfd();

            if(FD_ISSET(fd,&rfds))
            {
                if(fd == _listensock->GetSockfd())//处理连接就绪
                {
                    std::string ip;
                    uint16_t port;
                    //此时连接就一定不会阻塞了，因为确定了连接的读实际是存在的！
                    Socket* sock = _listensock->AcceptConnection(&ip,&port);//连接成功后
                    if(!sock)
                    {
                        lg.LogMessage(Error,"accept error");
                        continue;
                    }
                    lg.LogMessage(Info,"get a client,client info is# %s:%d,fd:%d\n",ip.c_str(),port,sock->GetSockfd());

                    //还不能直接进行 读 写
                    //因为假如没传送数据就仍然会堵塞
                    //所以就需要把获取的连接再次进行管理，管理的就是rfds中就绪的，将rfds交给select管理
                    //而此处管理我们就需要再次添加一个成员变量 数组Socket* _rfds_arry[n]，将该连接的fd进行管理

                    //将新连接的套接字 存放到数组中
                    for(int i = 0;i <= N;i++)
                    {
                        if(i == N)
                        {
                            sock->CloseFd();
                            delete sock;
                            lg.LogMessage(Warning,"server is full\n");
                        }
                        else if(_rfds_arry[i] == nullptr)//找到nulptr位置填进值
                        {
                            _rfds_arry[i] = sock;
                            lg.LogMessage(Info, "get a new link, fd is : %d\n", sock->GetSockfd());
                            break;
                        }
                    }
                }
                else{
                    std::string buffer;
                    bool res = _rfds_arry[i]->Recv(&buffer,1024);
                    if(res){//
                        lg.LogMessage(Info,"client say# %s\n",buffer.c_str());//此处读取是有问题的，因为还需要考虑协议的问题，反序列哈、粘包问题
                        buffer = "你好 少年\n";
                        _rfds_arry[i]->Send(buffer);
                        buffer.clear();
                    }
                    else{
                        lg.LogMessage(Warning,"client quit maybe close or error close fd: %d\n",_rfds_arry[i]->GetSockfd());
                        _rfds_arry[i]->CloseFd();
                        _rfds_arry[i] = nullptr;
                        delete _rfds_arry[i];
                    }
                }
            }
        }
        
    }
public:
    SelectServer(int port = defualtport):_port(port),_listensock(new TcpSocket()),_isrunning(false)
    {}

    ~SelectServer(){}



    void InitServer()
    {
        _listensock->BuildListenSocketMethod(_port,defualtlog);//Server开启监听
        for(int i =0;i < N ;i++)
        {
            _rfds_arry[i] = nullptr;
        }
        //将listensock的fd先初始化放到第一个为值
        _rfds_arry[0] = _listensock.get();//get得到原始指针Socket
    }


    void loop()
    {
        _isrunning = true;
        while(_isrunning)
        {
            //1. 不能连接accept，而是需要把accept交给select
            //2. 创建select
            fd_set rfds;//创建读事件的set
            int litensockfd = _listensock->GetSockfd();//获取得到套接字的fd
            
            //2.1 创建fd_set
            //2.1.1 清空fd_set、将listensock的fd设置到读事件中！
            FD_ZERO(&rfds);//清空函数
            
            // 最大的fd + 1
            //2.2 创建timevalue
            struct timeval timeout = {1,0}; //设置为 5s 0us 5s检测

            //补充：
            //需要找到最大的fd，所以要遍历select管理的数组
            // int maxfd = 0;
            // for(int i = 0;i < N;i++)
            // {
            //     maxfd = max(maxfd,_rfds_arry[i]->GetSockfd());
            // }


            // 修改：
            int maxfd = 0;


            for(int i = 0;i < N;i++)
            {
                if(_rfds_arry[i] == nullptr) continue;
                else{//非空表示有Socket已就绪的fd，添加到rfds
                    FD_SET(_rfds_arry[i]->GetSockfd(),&rfds);//设置listensock套接字给到读事件
                    maxfd = max(maxfd,_rfds_arry[i]->GetSockfd());
                }

            }
            PrintDebug();

            int n = select(maxfd + 1,&rfds,nullptr,nullptr,/*&timeout*/nullptr);

            //timeout 输出型参数返回剩余时间
            //为 0 0 时将非阻塞 式的不断超时
            //nullptr为阻塞式
            switch (n)
            {
            case 0://select超时
                lg.LogMessage(Info," select timeout..., last time: %u.%u\n",timeout.tv_sec,timeout.tv_usec);
                break;
            case -1:// <0 select出错
                lg.LogMessage(Error,"select Error！！\n");
                break;
            default://正常就绪
                lg.LogMessage(Info,"select success，begin event handler last time: %u.%u\n",timeout.tv_sec,timeout.tv_usec);
                //建立accept连接
                // std::string ip;
                // uint16_t port;
                // _listensock->AcceptConnection(&ip,&port);输出型参数获取得到连接者的ip和端口
                //也可以 直接 连接
                HandlerEvent(rfds);//处理读事件
                break;
            }

        }
        _isrunning = false;
    }

    void PrintDebug(){
        std::cout<< "currentlt sock fd list：";
        for(int i = 0;i < N; i++)
        {
            if(_rfds_arry[i] == nullptr) continue;
            std::cout << _rfds_arry[i]->GetSockfd() << " ";
        }
        std::cout << std::endl;
    }

    void Stop()
    {
        _isrunning = false;
    }

private:
    std::unique_ptr<Socket> _listensock;
    uint16_t _port;    
    bool _isrunning;

//添加了新的成员变量，他在
    Socket* _rfds_arry[N];
};